package top.malaoshi.webmvc.annotation.check;

import top.malaoshi.sprout.ioc.DefaultSingletonBeanRegistry;
import top.malaoshi.webmvc.exception.AnnotationConfigException;
import top.malaoshi.webmvc.exception.SameNameBeanException;
import top.malaoshi.webmvc.info.BeanAnnotationInfo;
import top.malaoshi.webmvc.info.ClassInfo;
import top.malaoshi.webmvc.info.MethodInfo;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 校验bean的名字，规则如下：
 * 1. 取出 @Controller、@RestController、@Component、@Service、@Configuration 注解的value值，以该value值作为bean的名字
 *    如果value值为空，就将类名（首字母小写）作为bean的名字
 *    如果 bean的名字有相同的，就抛 SameNameBeanException 异常
 * 2. 取出 @Bean 注解的 value、name，以其作为 bean 的名字
 *    如果value值为空，就将方法名（首字母小写）作为bean的名字
 *    如果 bean的名字有相同的，就抛 SameNameBeanException 异常
 *
 */
public class CheckBeanName implements AnnotationCheck{

    DefaultSingletonBeanRegistry registry = DefaultSingletonBeanRegistry.me();
    /**
     * bean的名字
     */
    private Set<String> names = new HashSet<>();
    /**
     * 用于现实错误
     */
    private Map<String,Class> nameAndClasses = new HashMap<>();
    /**
     * 用于现实错误
     */
    private Map<String, Method> nameAndMethods = new HashMap<>();


    public void check() throws Exception{
        try {
            registry.forEachClassInfos(item -> {
                checkClassAnnotation(item);
                checkBeanAnnotation(item);
            });

        }finally {
            names.clear();
            nameAndClasses.clear();
            nameAndMethods.clear();
        }

    }

    /**
     * 校验类上的注解
     * @param classInfo
     */
    private void checkClassAnnotation(ClassInfo classInfo)  {
        Class<?> clazz = classInfo.getClazz();
        String name = classInfo.getName();
        if(names.contains(name)){
            throw new SameNameBeanException(name,clazz);
        }
        names.add(name);
        nameAndClasses.put(name, clazz);
    }

    /**
     * 校验该类中， @Bean 注解修饰的方法，校验规则：
     * 1. 校验 value与name是否相同，不同抛 AnnotationConfigException 异常
     * 2. 校验 value 中的值是否出现在 names 中，如果有，就抛 SameNameBeanException 异常
     *
     * @param classInfo
     */
    private void checkBeanAnnotation(ClassInfo classInfo){
        Class clazz = classInfo.getClazz();
        String className = clazz.getName();
        List<MethodInfo> methodInfos = classInfo.getBeanAnnotationMethodInfos();
        for(MethodInfo methodInfo : methodInfos){
            BeanAnnotationInfo beanAnnotationInfo = methodInfo.getBeanAnnotationInfo();
            String[] value = beanAnnotationInfo.getValue();
            String[] name = beanAnnotationInfo.getName();

            Method method = methodInfo.getMethod();
            String methodName = method.getName();

            // 校验 @Bean 注解的 value、name是否相同
            boolean res = Arrays.equals(value, name);
            if(!res){
                String msg = String.format("'%s . %s' 的 @Bean 注解，value与name不相同", className,methodName);
                throw new AnnotationConfigException(msg);
            }
            for(String item : value){
                if(names.contains(item)){
                    Class aClass = nameAndClasses.get(item);
                    Method aMethod = nameAndMethods.get(item);
                    if(aMethod == null){
                        throw new SameNameBeanException(item,
                                className,
                                methodName,
                                aClass.getName()

                        );

                    }else {
                        throw new SameNameBeanException(item,
                                className,
                                methodName,
                                aClass.getName(),
                                aMethod.getName()

                        );
                    }
                }
                names.add(item);
                nameAndClasses.put(item, clazz);
                nameAndMethods.put(item, method);
            }

        }
    }
//    public ClassInfo getClassInfoByName(String name){
//        for(ClassInfo classInfo : classInfos){
//            String nameOfClassInfo = classInfo.getName();
//            if(nameOfClassInfo.equals(name)){
//                return classInfo;
//            }
//        }
//        return null;
//    }
//    private void checkBeanAnnotation(ClassInfo classInfo){
//        Class<?> beanClass = classInfo.getClazz();
//
//        Method[] methods = beanClass.getDeclaredMethods();
//        for(Method method : methods){
//
//            if(method.isAnnotationPresent(Bean.class)){
//                Bean ann = method.getAnnotation(Bean.class);
//                String[] value = ann.value();
//                String[] name = ann.name();
//
//                String[] valuesAndNames = ArrayUtil.concatAll(value, name);
//                // 如果没有value、name配置
//                if(valuesAndNames == null || valuesAndNames.length == 0){
//                    String beanName = ClassUtil.lowerFirstChar(method.getName());
//                    if(names.contains(beanName)){
//                        throw new SameNameBeanException(beanName,beanClass,method);
//                    }
//                    names.add(beanName);
//                }else{
//                    for(String valueAndName : valuesAndNames){
//                        if(names.contains(valueAndName)){
//                            throw new SameNameBeanException(valueAndName,beanClass,method);
//                        }
//                        names.add(valueAndName);
//                    }
//                }
//            }
//
//        }
//
//
//    }

}
